
use rocket::http::ContentType;
use rocket::{Request, Config, post};
use rocket::data::{Limits, ByteUnit};
use crate::conf_init::{restful_conf};
use std::net::IpAddr;
use tokio::runtime;
use rocket::config::Shutdown;
use std::collections::HashMap;
use crate::indicators::{restful_indicators, restful_channel_input};
use crate::restful::back_info::BackInfo;
use std::sync::RwLock;
use std::str::FromStr;
use std::io::Write;
use crate::lazy_static::__Deref;
use crate::restful::reg_work_space::{register_group_dispatch_sync};

mod back_info;
mod reg_work_space;

#[derive(Deserialize)]
pub struct RestfulConf{
    pub restful_ip_port: String,
    pub restful_store_file: String,
    pub threads: usize,
}

struct RestuflFunc{
    register_restful_fn_post: Option<fn(mode: &str, data: &[u8])->Result<(String/*给调用者返回的字符串*/,Option<String>/*落盘的字符串*/), String>>,
    register_restful_fn_get:  Option<fn()->String>,
}
impl RestuflFunc{
    fn new(f_post: Option<fn(mode: &str, data: &[u8])->Result<(String/*给调用者返回的字符串*/,Option<String>/*落盘的字符串*/), String>>,
           f_get: Option<fn()->String>)->Self{
        Self{
            register_restful_fn_post: f_post,
            register_restful_fn_get: f_get,
        }
    }
}

lazy_static!{
    static ref RESTFUL_LIST: RwLock<HashMap<String, RestuflFunc>> = RwLock::new(HashMap::new());
    static ref RESTFUL_STORE: RwLock<HashMap<String, HashMap<String, String>>> = RwLock::new(HashMap::new());
}

fn restful_store_write_back(){
    match RESTFUL_STORE.read(){
        Ok(t) => {
            match serde_json::to_string(t.deref()){
                Ok(t) => {
                    let restful_conf = restful_conf();
                    match std::fs::File::create(std::path::Path::new(&restful_conf.restful_store_file)){
                        Ok(mut fs) => {
                            if let Err(e) = fs.write(t.as_bytes()){
                                error!("restful_store_write_back write err {:?}", e);
                                return;
                            }
                            if let Err(e) = fs.flush(){
                                error!("restful_store_write_back flush err {:?}", e);
                                return;
                            }
                        },
                        Err(e) => {
                            error!("restful_store_write_back create file err {:?}", e);
                        },
                    }
                },
                Err(e) => {
                    error!("restful_store_write_back serde_json::to_string err {:?}", e);
                },
            }
        },
        Err(e) => {
            error!("RESTFUL_STORE RwLock write err: {:?}", e);
            std::process::exit(-1);
        },
    }
}

fn restful_store_set(group: &str, name:&str, value: String){

    match RESTFUL_STORE.write(){
        Ok(mut t) => {
            match t.get_mut(group){
                Some(a)=>{
                    (*a).insert(name.to_string(), value);
                }
                None=>{
                    t.insert(group.to_string(), HashMap::from([(name.to_string(),value)]));
                }
            }   
        },
        Err(e) => {
            error!("RESTFUL_STORE RwLock write err: {:?}", e);
            std::process::exit(-1);
        },
    }
    restful_store_write_back();
}

fn restful_store_del(group: &str, name : &str){
    match RESTFUL_STORE.write(){
        Ok(mut t) => {
            match t.get_mut(group){
                Some(a)=>{
                    (*a).remove(name);
                    if (*a).len() == 0 {
                        t.remove(group);
                    }
                }
                None=>{
                    return
                }
            } 
            
        },
        Err(e) => {
            error!("RESTFUL_STORE RwLock write err: {:?}", e);
            std::process::exit(-1);
        },
    }
    restful_store_write_back();
}

fn restful_register(group: &str,
                    f_post: Option<fn(mode:&str, data: &[u8])->Result<(String,Option<String>),String>>,
                    f_get: Option<fn()->String>){
    if let Ok(mut restful_list) = RESTFUL_LIST.write(){
        restful_list.insert(group.to_string(), RestuflFunc::new(f_post, f_get));
    }else{
        error!("RESTFUL_LIST RwLock write err");
        std::process::exit(-1);
    }
}

pub fn restful_run(){
    restful_register_all();

    let restful_conf = restful_conf();

    let ip_port: Vec<&str> = restful_conf.restful_ip_port.split(":").collect();
    if ip_port.len() != 2{
        error!("restful_ip_port [{}] err", restful_conf.restful_ip_port);
        std::process::exit(-1);
    }

    let restful_ip = match IpAddr::from_str(ip_port[0]){
        Ok(t) => {t},
        Err(e) => {
            error!("restful [{}] IpAddr err [{:?}]", ip_port[0], e);
            std::process::exit(-1);
        },
    };

    let restful_port = match u16::from_str(ip_port[1]){
        Ok(t) => {t},
        Err(e) => {
            error!("restful [{}] err [{:?}]", ip_port[1], e);
            std::process::exit(-1);
        },
    };

    let limits= Limits::new()
        .limit("form", Limits::FORM)
        .limit("data-form", Limits::DATA_FORM)
        .limit("file", Limits::FILE)
        .limit("string", ByteUnit::Mebibyte(10))
        .limit("bytes", ByteUnit::Mebibyte(10))
        .limit("json", ByteUnit::Mebibyte(10))
        .limit("msgpack", ByteUnit::Mebibyte(10));

    let rock_config = Config{
        address: restful_ip,
        port: restful_port,
        limits: limits,
        shutdown: Shutdown{
            ctrlc: false,
            ..Default::default()
        },
        ..Default::default()
    };

    let rocket = rocket::custom(rock_config)
            .mount("/", routes![hi_get, hi_post, index]);

    let restful_runtime = match runtime::Builder::new_multi_thread()
            .thread_name("restful")
            .worker_threads(2)
            .enable_io()
            .build(){
        Ok(t) => {t},
        Err(e) => {
            error!("restful build Runtime err: {:?}", e);
            std::process::exit(-1);
        },
    };

    match restful_runtime.block_on(async{
        info!("restful wait");
        rocket.launch().await
    }){
        Ok(t) => {},
        Err(e) => {
            error!("restful_run runtime block_on err {:?}", e);
            std::process::exit(-1);
        },
    }
}

fn restful_register_all(){
    restful_register("stat", None, Some(restful_indicators));
    restful_register("input", None, Some(restful_channel_input));
    restful_register("register", Some(register_group_dispatch_sync), None);
}

#[catch(400)]
fn not_found(req: &Request) -> String {
    format!("uri [{}] not support", req.uri())
}

#[post("/<group>/<name>", data = "<user>")]
fn hi_post(group: String, name: String, user: &[u8]) -> String {
    info!("restful Post [{}] [{}] [{:?}]", group, name, user);

    if let Ok(restful_list) = RESTFUL_LIST.read(){
        if let Some(restful_func) = restful_list.get(group.as_str()){
            if let Some(fn_post) = restful_func.register_restful_fn_post {
                let res = (fn_post)(name.as_str(), user);
                match res {
                    Ok((b, s)) => {
                        if let Some(s) = s {
                            restful_store_set(group.as_str(), name.as_str(), s);
                        }
                        b
                    },
                    Err(b) => {
                        restful_store_del(group.as_str(), name.as_str());
                        b
                    },
                }
            }else{
                format!("Post /{}/{} not support", group, name)
            }
        }else{
            BackInfo::new_err(format!("{} not support", group))
        }
    }else{
        error!("RestuflList RwLock read err");
        std::process::exit(-1);
    }
}

#[get("/<group>")]
fn hi_get(group: String) -> String {
    info!("restful Get [{}]", group);

    if let Ok(restful_list) = RESTFUL_LIST.read(){
        if let Some(restful_func) = restful_list.get(group.as_str()){
            if let Some(fn_get) = restful_func.register_restful_fn_get {
                (fn_get)()
            }else{
                format!("Get /{} not support", group)
            }
        }else{
            format!("Get /{} not support", group)
        }
    }else{
        error!("RESTFUL_LIST RwLock read err");
        std::process::exit(-1);
    }
}

#[get("/")]
fn index() -> (ContentType, String) {
    let mut html = String::new();
    if let Ok(restful_list) = RESTFUL_LIST.read(){
        for (name, func) in restful_list.iter() {
            if let Some(fn_get) = func.register_restful_fn_get {
                html += format!("<a href= \"{}\" target=\"_blank\">{}</a ><br/>", name, name).as_str();
            }
        }
    }else{
        error!("RESTFUL_LIST RwLock read err");
        std::process::exit(-1);
    }

    (
        ContentType::HTML,
        html
    )
}
